/*	talmud - The TALMUD roguelike
 * Copyright (C) 2012, talmud contributors
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of Version 2 of the GNU General Public License as published
 *  by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *  FITNESS FOR A PARTICULAR PURPOSE. See Version 2 of the GNU General Public
 *  License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "playermanage.h"
#include "talmud.h"
#include "race.h"
#include "class.h"
#include "rand.h"
#include "draw.h"
#include "config.h"
#include "monstermanage.h"
#include "item.h"
#include "itemmanage.h"

static const uint32_t XLVL_XP[] = {
	0,	// 0
	0,	// 1
	50,	// 2
	100,	// 3
	150,	// 4
	200,	// 5
	400,	// 6
	600,	// 7
	800,	// 8
	1000,	// 9
	1500,	// 10
	2000,	// 11
	2500,	// 12
	3000,	// 13
	4000,	// 14
	5000,	// 15
};
static const uint8_t XLVL_CAP = 15;

static uint8_t barehandwarn;
uint8_t invcount;
item_t inv[INVMAX];
uint8_t invchar[INVMAX];
uint8_t invwielded;
uint8_t invworn[S_COUNT];

void Lucky(int8_t adj)
{
	if (adj < 0)
	{
		if (plyr.luck + adj > plyr.luck)
		{
			plyr.luck = -128;
		}
		else
		{
			plyr.luck += adj;
		}
	}
	else
	{
		plyr.luck += adj;
		if (plyr.luck > 5)
		{
			plyr.luck = 5;
		}
	}
}

void Religious(int8_t adj)
{
	if (adj < 0)
	{
		if (plyr.relig + adj < -(plyr.xlvl + 1))
		{
			plyr.relig = -(plyr.xlvl + 1);
		}
		else
		{
			plyr.relig += adj;
		}
	}
	else
	{
		if (plyr.relig + adj > (plyr.xlvl + 1))
		{
			plyr.relig = (plyr.xlvl + 1);
		}
		else
		{
			plyr.relig += adj;
		}
	}
}

void PromptID(uint8_t itype)
{
	char desc[64];
	char fc;

	if (itypeided[itype] || strcmp(itypedesc[itype], ""))
	{
		return;
	}
	fc = ITEMDEFS[itype].name[0];
	if (fc == 'a' || fc == 'e' || fc == 'i' || fc == 'o' || fc == 'u')
	{
		sprintf(desc, "an %s", UnknownItemDesc(itype));
	}
	else
	{
		sprintf(desc, "a %s", UnknownItemDesc(itype));
	}
	AddMsg("$PROMPT:ITYPEDESC:%i$What do you want to call %s? ", itype, desc);
}

void Identify(uint8_t itype)
{
	itypeided[itype] = 1;
}

static int CompareInv(const void *va, const void *vb)
{
	const uint8_t *a = (const uint8_t*)va;
	const uint8_t *b = (const uint8_t*)vb;

	return (ITEMDEFS[inv[*a].type].type - ITEMDEFS[inv[*b].type].type);
}

void SortInventory(void)
{
	uint8_t indices[INVMAX];
	uint8_t idx;
	item_t oldinv[INVMAX];
	uint8_t oldinvchar[INVMAX];
	uint8_t oldinvwielded;
	uint8_t oldinvworn[S_COUNT];
	uint8_t slot;

	for (idx = 0; idx < invcount; idx++)
	{
		indices[idx] = idx;
	}

	qsort(indices, invcount, sizeof(uint8_t), CompareInv);

	memcpy(oldinv, inv, sizeof(item_t) * invcount);
	memcpy(oldinvchar, invchar, sizeof(uint8_t) * invcount);
	oldinvwielded = invwielded;
	memcpy(oldinvworn, invworn, sizeof(uint8_t) * S_COUNT);

	for (idx = 0; idx < invcount; idx++)
	{
		if (oldinvwielded == indices[idx])
		{
			invwielded = idx;
		}
		for (slot = 0; slot < S_COUNT; slot++)
		{
			if (oldinvworn[slot] == indices[idx])
			{
				invworn[slot] = idx;
			}
		}
		memcpy(&inv[idx], &oldinv[indices[idx]], sizeof(item_t));
		invchar[idx] = oldinvchar[indices[idx]];
	}
}

uint8_t ItemWorn(uint8_t invidx)
{
	uint8_t idx;

	if (invidx == INV_NONE)
	{
		return 0;
	}
	for (idx = 0; idx < S_COUNT; idx++)
	{
		if (invidx == invworn[idx])
		{
			return 1;
		}
	}

	return 0;
}

void InitPlayer(uint8_t race, uint8_t class)
{
	uint8_t idx;

	memset(&plyr, 0, sizeof(player_t));

	barehandwarn = 0;
	invwielded = INV_NONE;
	for (idx = 0; idx < INVMAX; idx++)
	{
		invchar[idx] = ' ';
	}
	for (idx = 0; idx < S_COUNT; idx++)
	{
		invworn[idx] = INV_NONE;
	}
	plyr.class = class;
	plyr.maxhp = 1;
	plyr.st = 10;
	plyr.dx = 10;
	plyr.co = 10;
	plyr.in = 10;
	plyr.wi = 10;
	plyr.ch = 10;
	plyrmon.cintrins = I_NONE;
	plyrmon.flags = F_NONE;
	plyrmon.race = race;
	plyrmon.hp = plyr.maxhp;
}

void BuildPlayer()
{
	invcount = 0;
	if (plyrmon.race == R_HUMAN)
	{
		invwielded = invcount;
		invchar[invcount] = 'a';
		inv[invcount] = BuildItem(ID_DAGGER, rn2(3), B_NONE);
		inv[invcount].count = 1;	// daggers may randomly be generated in stacks,
						// we want to preclude this
		invcount++;

		invchar[invcount] = 'b';
		invworn[S_BODY] = invcount;
		inv[invcount++] = BuildItem(ID_LEATHER_ARMOR, rn2(3), B_NONE);

		plyr.st = 11 + rn1(8);
		plyr.dx = 11 + rn1(8);
		plyr.co = 11 + rn1(8);
	}
	else
	{
		AddMsg("You feel an existential crisis coming on. It's deadly!");
		GameOver();
		return;
	}

	switch (plyr.class)
	{
		case C_MAGEWEAVER:
			plyr.in = 13 + rn1(6);
			plyr.wi = 12 + rn1(6);
			plyr.ch = 7 + rn1(6);
			break;
		default:
			AddMsg("You feel that your existence is a fluke. You die.");
			GameOver();
			return;
	}
}

void PlayerLevels(void)
{
	uint8_t idx;

	for (idx = 1; idx < XLVL_CAP; idx++)
	{
		if (plyr.xp < XLVL_XP[idx + 1])
		{
			break;
		}
	}
	plyr.xlvl = idx;

	switch (plyrmon.race)
	{
		case R_HUMAN:
			plyr.maxhp = 14 + plyr.xlvl * 2 + rn1(8);
			break;
		case R_ELF:
			plyr.maxhp = 10 + plyr.xlvl * 2 + rn1(6);
			break;
		default:
			plyr.maxhp = 8;
			break;
	}

	switch (plyr.class)
	{
		case C_MAGEWEAVER:
			plyr.maxhp += plyr.xlvl * 2;
			break;
		default:
			plyr.maxhp += plyr.xlvl;
			break;
	}

	plyrmon.hp = plyr.maxhp;
}

void PlayerEnters(uint8_t whence)
{
	uint8_t moncount;
	uint8_t diff;
	uint8_t idx;

	if (!plyrdlvl.exists)
	{
		switch (plyrmon.branch)
		{
			case B_DUNGEONS:
				if (plyrmon.dlvl != 1)
				{
					PlyrExp(10);
				}
				GenerateDungeon(&plyrdlvl, plyrmon.dlvl);
				break;
			case B_CATACOMBS:
				PlyrExp(15);
				GenerateCatacomb(&plyrdlvl, plyrmon.dlvl);
				break;
			default:
				AddMsg("You discover a new world! You die of shock.");
				GameOver();
				return;
		}
		AddMsg("Welcome to level %i of %s.", plyrmon.dlvl, plyrbrnch.name);

		if (plyrmon.branch == B_DUNGEONS && plyrmon.dlvl == BRANCHDEFS[B_DUNGEONS].depth)
		{
			AddMsg("You feel a strange sense of finality.");
			GenSpecMon(MON_BOSS);
		}

		moncount = rn3(5);
		if (!rn1(8))
		{
			AddMsg("You hear snarls in the distance.");
			do
			{
				moncount += rn2(9);
			} while (moncount < 4);
			diff = ((plyr.xlvl + plyrmon.dlvl) >> 1) + rn2(3);
		}
		else
		{
			diff = (plyr.xlvl + plyrmon.dlvl) >> 1;
		}
#ifdef _WIZARD
		AddMsg("This level was created with %i monsters.", moncount);
#endif
		for (idx = 0; idx < moncount; idx++)
		{
			MonGen(diff);
		}
	}

	if (whence == K_MOVEDN)
	{
		plyrmon.x = plyrdlvl.upstair.x;
		plyrmon.y = plyrdlvl.upstair.y;
	}
	else if (whence == K_MOVEUP)
	{
		plyrmon.x = plyrdlvl.dnstair.x;
		plyrmon.y = plyrdlvl.dnstair.y;
	}
	else
	{
		do
		{
			plyrmon.x = rn1(DLVL_W);
			plyrmon.y = rn1(DLVL_H);
		} while (plyrtile != T_FLOOR);
	}
}

uint8_t GoUp(void)
{
	if (plyrtile != T_UPSTAIR)
	{
		AddMsg("You see no way to go up here.");
		return 0;
	}
	plyrmon.dlvl--;
	AddMsg("You ascend the staircase.");
	PlayerEnters(K_MOVEUP);
	return 1;
}

uint8_t GoDn(void)
{
	if (plyrtile != T_DNSTAIR)
	{
		AddMsg("You see no way to go down here.");
		return 0;
	}
	plyrmon.dlvl++;
	AddMsg("You descend the staircase.");
	PlayerEnters(K_MOVEDN);
	return 1;
}

static void PlyrAtk(uint16_t monidx)
{
	int8_t roll;
	uint8_t attacks;
	uint8_t hd;
	uint8_t hdcount;
	int8_t tohit;
	uint8_t idx;
	uint8_t idx2;
	int8_t dmg;
	uint8_t killed;

	if (invwielded == INV_NONE)
	{
		if (!barehandwarn)
		{
			AddMsg("You begin attacking monsters with your bare hands.");
			barehandwarn = 1;
		}
		attacks = 1;
		hdcount = 2;
		hd = 2;
		tohit = -2;
	}
	else
	{
		attacks = weapondef.atkcount + rn2(weapon.holiness == B_HOLY);
		tohit = weapondef.tohit + weapon.enchant + ((weapon.holiness == B_HOLY) ? 1 : 0) + ((weapon.holiness == B_CURSED) ? -2 : 0) + (plyr.xlvl / 2);
	}
	tohit += ((plyr.luck > 0) ? 1 : ((plyr.luck < 0) ? (plyr.luck / 2 - 1) : 0));

	if (mon[monidx].flags & F_ASLEEP)
	{
		AddMsg("You startle %s awake.", monterm(monidx));
		mon[monidx].flags &= ~F_ASLEEP;
	}

	for (idx = 0; idx < attacks; idx++)
	{
		if (invwielded != INV_NONE)
		{
			hdcount = weapondef.hdcount[idx];
			hd = weapondef.hd[idx] + weapon.enchant;
			if (hd < 2)
			{
				hd = 2;
			}
			if (idx == weapondef.atkcount && weapon.knownholy != weapon.holiness)
			{
				AddMsg("You feel a righteous presence guiding your hands.");
				weapon.knownholy = weapon.holiness;
			}
		}
		roll = rn1(20) + 1;
		if (roll + tohit < 0)
		{
			roll = 1;
		}
		else
		{
			roll += tohit;
		}
		if (roll < monac(monidx))
		{
			AddMsg("You miss %s.", monterm(monidx));
			continue;
		}
		AddMsg("You hit %s.", monterm(monidx));
		dmg = 0;
		for (idx2 = 0; idx2 < hdcount; idx2++)
		{
			dmg += rn1(hd) + 1;
		}
		dmg += plyrstrbns;
		if (dmg < 1)
		{
			dmg = 1;
		}
		killed = MonDmg(monidx, dmg);
		if (killed)
		{
			AddMsg("You kill %s.", monterm(monidx));
			PlyrExp(RACEDEFS[mon[monidx].race].diff * 4);
			break;
		}
	}
}

static void PlyrDTrap(void)
{
	uint8_t roll;

	AddMsg("Click!");
	roll = rn1(4 + ((plyr.luck < 0) ? (-plyr.luck) : 0));
	if (!roll)
	{
		roll = rn1(2);
		if (roll == 0)
		{
			AddMsg("A dart misses you.");
		}
		else
		{
			AddMsg("Fortunately, the dart trap misfires.");
		}
		AddMsg("There is a dart trap here.");
		PlyrExp(2);
		return;
	}
	AddMsg("You are hit by a dart.");
	PlyrExp(1);
	// 1d4 damage
	roll = 1 + rn1(4);
	PlyrDmg(roll);
	if (plyr.luck <= 2)
	{
		roll = rn1(2 - plyr.luck);
		if (roll != 0)
		{
			AddMsg("The dart was poisoned!");
			PlyrDmg(2 + rn1(8));
		}
	}
	AddMsg("There is a dart trap here.");
}

uint8_t MovePlyr(int8_t dx, int8_t dy)
{
	uint8_t px;
	uint8_t py;
	uint8_t tile;
	uint16_t monidx;
	uint8_t *itemids;
	uint16_t itemcount;

	px = plyrmon.x + dx;
	py = plyrmon.y + dy;
	monidx = GetMonAt(px, py);
	if (monidx != MON_NULL)
	{
		PlyrAtk(monidx);
		return 1;
	}
	tile = dtile(px, py);
	if (impassable(tile))
	{
		return 0;
	}
	plyrmon.x = px;
	plyrmon.y = py;
	itemcount = GetItemsAt(plyrmon.x, plyrmon.y, &itemids);
	if (itemcount > 1)
	{
		AddMsg("There are items here.");
	}
	else if (itemcount == 1)
	{
		AddMsg("You see here %s", ItemDesc(&items[itemids[0]], INV_NONE));
	}
	free(itemids);
	switch (tile)
	{
		case T_POOL:
			AddMsg("There is a pool of water here.");
			break;
		case T_IDOL:
			AddMsg("There is an idol to Bahamut here.");
			break;
		case T_UPSTAIR:
			AddMsg("There is a staircase up here.");
			break;
		case T_DNSTAIR:
			AddMsg("There is a staircase down here.");
			break;
		case T_DTRAP:
			PlyrDTrap();
			break;
	}

	return 1;
}

void UpdatePlyr(void)
{
	uint8_t idx;
	uint8_t roll;

	plyrmon.speedcounter += RACEDEFS[plyrmon.race].speed;

	plyr.ac = 0;
	for (idx = 0; idx < invcount; idx++)
	{
		if (worn(idx))
		{
			plyr.ac += ITEMDEFS[inv[idx].type].baseac + inv[idx].enchant;
		}
	}
	if (plyrmon.hp < plyr.maxhp && !rn1(8))
	{
		roll = 1 + rn2(plyr.xlvl);
		plyrmon.hp += roll;
		if (plyrmon.hp > plyr.maxhp)
		{
			plyrmon.hp = plyr.maxhp;
		}
	}
}

void DrawPlyr(void)
{
	SetColor(A_INTENSE | A_WHITE);
	DrawChar(plyrmon.x, DLVL_BASEY + plyrmon.y, '@');
}

uint8_t PlyrDmg(uint16_t dmg)
{
	if (plyrmon.hp > dmg)
	{
		plyrmon.hp -= dmg;
		return 0;
	}
	plyrmon.hp = 0;
	AddMsg("You die.");
	GameOver();
	return 1;
}

void PlyrHeal(uint16_t healing)
{
	if (plyrmon.hp + healing > plyr.maxhp)
	{
		plyrmon.hp = plyr.maxhp;
	}
	else
	{
		plyrmon.hp += healing;
	}
}

void PlyrInvis(uint16_t turns)
{
	plyrmon.cintrins |= IM(I_INVIS);
	if (plyr.invisturns + turns < plyr.invisturns)
	{
		plyr.invisturns = 0xFFFF;
	}
	else
	{
		plyr.invisturns += turns;
	}
}

void PlyrInfravis(uint16_t turns)
{
	plyrmon.cintrins |= IM(I_INFRAVIS);
	if (plyr.infravisturns + turns < plyr.infravisturns)
	{
		plyr.infravisturns = 0xFFFF;
	}
	else
	{
		plyr.infravisturns += turns;
	}
}

void PlyrParalyzed(uint16_t turns)
{
	if (plyrmon.resists & IM(I_PARALYSIS))
	{
		return;
	}
	plyrmon.cintrins |= IM(I_PARALYSIS);
	if (plyr.paralyzedturns + turns < plyr.paralyzedturns)
	{
		plyr.paralyzedturns = 0xFFFF;
	}
	else
	{
		plyr.paralyzedturns += turns;
	}
}

void PlyrSleeps(uint16_t turns)
{
	if (plyrmon.resists & IM(I_SLEEP))
	{
		return;
	}
	plyrmon.cintrins |= IM(I_SLEEP);
	if (plyr.sleepturns + turns < plyr.sleepturns)
	{
		plyr.sleepturns = 0xFFFF;
	}
	else
	{
		plyr.sleepturns += turns;
	}
}

void PlyrConf(uint16_t turns)
{
	if (plyrmon.resists & IM(I_CONFUSION))
	{
		return;
	}
	plyrmon.cintrins |= IM(I_CONFUSION);
	if (plyr.confturns + turns < plyr.confturns)
	{
		plyr.confturns = 0xFFFF;
	}
	else
	{
		plyr.confturns += turns;
	}
}

uint8_t PlyrDead(void)
{
	return (plyrmon.hp <= 0);
}

void PlyrExp(uint32_t xp)
{
	uint8_t oldxl;
	uint8_t idx;

	if (plyr.xp + xp < plyr.xp)
	{
		plyr.xp = 0xFFFFFFFF;
	}
	else
	{
		plyr.xp += xp;
	}
	if (plyr.xp >= XLVL_XP[plyr.xlvl + 1])
	{
		oldxl = plyr.xlvl;
		PlayerLevels();
		for (idx = oldxl + 1; idx <= plyr.xlvl; idx++)
		{
			AddMsg("Welcome to experience level %i", idx);
		}
	}
}

void PlyrPickup(uint32_t itemidx)
{
	char ch;
	uint8_t idx;
	uint16_t remcount;

	remcount = items[itemidx].count;
	for (idx = 0; idx < invcount; idx++)
	{
		if (inv[idx].type != items[itemidx].type)
		{
			continue;
		}
		if (inv[idx].count + remcount > ITEMDEFS[items[itemidx].type].maxstack)
		{
			remcount -= (ITEMDEFS[items[itemidx].type].maxstack - inv[idx].count);
			inv[idx].count = ITEMDEFS[items[itemidx].type].maxstack;
		}
		else
		{
			inv[idx].count += remcount;
			AddMsg("%c - %s", invchar[idx], ItemDesc(&inv[idx], idx));
			DeleteItem(itemidx);
			return;
		}
	}

	items[itemidx].count = remcount;

	if (invcount >= INVMAX)
	{
		AddMsg("Your knapsack is full.");
		return;
	}
	memcpy(&inv[invcount], &items[itemidx], sizeof(item_t));
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		for (idx = 0; idx < invcount; idx++)
		{
			if (ch == invchar[idx])
			{
				break;
			}
		}
		if (idx == invcount)
		{
			break;
		}
	}
	invchar[invcount] = ch;
	AddMsg("%c - %s", ch, ItemDesc(&inv[invcount], invcount));
	invcount++;
	SortInventory();
	DeleteItem(itemidx);
}

static void RemoveFromInv(uint8_t invidx)
{
	uint8_t idx;

	if (invidx == invwielded)
	{
		invwielded = INV_NONE;
	}
	else if (invwielded == invcount - 1)
	{
		invwielded = invidx;
	}
	for (idx = 0; idx < S_COUNT; idx++)
	{
		if (invidx == invworn[idx])
		{
			invworn[idx] = INV_NONE;
		}
		else if (invworn[idx] == invcount - 1)
		{
			invworn[idx] = invidx;
		}
	}
	memcpy(&inv[invidx], &inv[invcount - 1], sizeof(item_t));
	invchar[invidx] = invchar[invcount - 1];
	invcount--;
	SortInventory();
}

static void DecrInv(uint8_t invidx, uint16_t count)
{
	if (inv[invidx].count > count)
	{
		inv[invidx].count -= count;
	}
	else
	{
		RemoveFromInv(invidx);
	}
}

void PlyrDrop(uint8_t invidx)
{
	AddMsg("You drop %s", ItemDesc(&inv[invidx], invidx));
	AddItemAt(&inv[invidx], plyrmon.x, plyrmon.y, plyrmon.dlvl, plyrmon.branch);
	RemoveFromInv(invidx);
}

uint8_t PlyrQuaff(uint8_t invidx)
{
	uint8_t idx;
	uint8_t itemtype;
	uint8_t pottype;
	uint8_t buc;
	uint8_t count;

	itemtype = inv[invidx].type;
	if (ITEMDEFS[itemtype].type != I_POTION)
	{
		AddMsg("You can't drink that!");
		return 0;
	}

	buc = inv[invidx].holiness;
	pottype = ITEMDEFS[itemtype].slot;
	switch (pottype)
	{
		case P_WATER:
			if (buc == B_NONE)
			{
				AddMsg("You quench your thirst.");
				Identify(inv[invidx].type);
			}
			else if (buc == B_CURSED)
			{
				Lucky(-1);
				PlyrExp(5);
				AddMsg("You feel unlucky.");
				PromptID(inv[invidx].type);
			}
			else if (buc == B_UNHOLY)
			{
				Religious(-10);
				PlyrExp(5);
				AddMsg("You shudder.");
				PromptID(inv[invidx].type);
			}
			else if (buc == B_HOLY)
			{
				Religious(3);
				PlyrExp(5);
				AddMsg("You glow with an inner warmth.");
				PromptID(inv[invidx].type);
			}
			break;
		case P_HEALING:
			if (buc == B_NONE)
			{
				AddMsg("You feel better.");
				Identify(inv[invidx].type);
				PlyrHeal(rn1(8) + rn1(8) + 2);
			}
			else if (buc == B_CURSED)
			{
				AddMsg("You feel a little better.");
				Identify(inv[invidx].type);
				PlyrHeal(rn1(3) + rn1(3) + 2);
			}
			else if (buc == B_UNHOLY)
			{
				AddMsg("You feel worse.");
				PlyrDmg(rn1(4) + 1);
				PromptID(inv[invidx].type);
			}
			else if (buc == B_HOLY)
			{
				AddMsg("You feel much better.");
				Identify(inv[invidx].type);
				PlyrHeal(rn1(10) + rn1(10) + 6);
			}
			break;
		case P_FULLHEALING:
			if (buc == B_NONE || buc == B_CURSED || buc == B_HOLY)
			{
				AddMsg("You feel restored to health.");
				Identify(inv[invidx].type);
				PlyrHeal(250);
			}
			else
			{
				AddMsg("Your insides burn within you!");
				PromptID(inv[invidx].type);
				PlyrDmg(7 + rn1(20));
			}
			break;
		case P_INVISIBILITY:
			if (buc == B_NONE)
			{
				AddMsg("You turn invisible.");
				Identify(inv[invidx].type);
				PlyrInvis(50 + rn3(50));
			}
			else if (buc == B_CURSED)
			{
				AddMsg("Your body takes on a strange transparency.");
				PromptID(inv[invidx].type);
				PlyrInvis(10 + rn3(25));
			}
			else if (buc == B_UNHOLY)
			{
				AddMsg("You feel the breath of an invisible monster on your neck.");
				PromptID(inv[invidx].type);
			}
			else if (buc == B_HOLY)
			{
				AddMsg("You turn invisible.");
				Identify(inv[invidx].type);
				PlyrInvis(100 + rn3(200));
			}
			break;
		case P_PARALYSIS:
			if (buc == B_NONE)
			{
				AddMsg("Your muscles refuse to obey you.");
				PromptID(inv[invidx].type);
				PlyrParalyzed(25 + rn3(50));
			}
			else if (buc == B_CURSED)
			{
				AddMsg("Your muscles are frozen.");
				PromptID(inv[invidx].type);
				PlyrParalyzed(50 + rn3(50));
			}
			else if (buc == B_UNHOLY)
			{
				AddMsg("Your body convulses.");
				PromptID(inv[invidx].type);
				PlyrParalyzed(150 + rn1(150));
			}
			else if (buc == B_HOLY)
			{
				AddMsg("Your muscles momentarily twitch.");
				PromptID(inv[invidx].type);
				PlyrParalyzed(1);
			}
			AddMsg("You are paralyzed.");
			break;
		case P_SLEEPING:
			if (buc == B_NONE || buc == B_CURSED || buc == B_HOLY)
			{
				AddMsg("You fall asleep.");
				Identify(inv[invidx].type);
				PlyrSleeps(25 + rn3(100));
			}
			else
			{
				AddMsg("You fall into a deep, magical sleep.");
				Identify(inv[invidx].type);
				PlyrSleeps(100 + rn1(200));
			}
			AddMsg("You are asleep.");
			break;
		case P_CONFUSION:
			if (buc == B_NONE || buc == B_HOLY)
			{
				AddMsg("You feel confused.");
				Identify(inv[invidx].type);
				PlyrConf(10 + rn1(20));
			}
			else if (buc == B_CURSED)
			{
				AddMsg("Your head spins.");
				PromptID(inv[invidx].type);
				PlyrConf(25 + rn1(40));
			}
			else if (buc == B_UNHOLY)
			{
				if (plyrmon.resists & IM(I_SLEEP))
				{
					AddMsg("Your head spins violently!");
				}
				else
				{
					AddMsg("Your head spins violently! You faint.");
					PlyrSleeps(rn1(10));
				}
				PromptID(inv[invidx].type);
				PlyrConf(80 + rn3(100));
			}
			break;
		case P_POISON:
			if (buc == B_NONE)
			{
				AddMsg("Poison floods through your veins.");
				Identify(inv[invidx].type);
				PlyrDmg(rn1(20) + rn1(8) + 2);
			}
			else if (buc == B_CURSED)
			{
				AddMsg("Tastes like hemlock.");
				PromptID(inv[invidx].type);
				PlyrDmg(rn1(8) + 1);
			}
			else if (buc == B_UNHOLY)
			{
				AddMsg("Poison withers your arteries!");
				Identify(inv[invidx].type);
				PlyrDmg(rn1(20) + rn1(20) + 8);
				if (plyr.st > 3)
				{
					plyr.st--;
				}
				if (plyr.co > 3)
				{
					plyr.co--;
				}
			}
			else if (buc == B_HOLY)
			{
				AddMsg("Poison floods through your veins.");
				Identify(inv[invidx].type);
				PlyrDmg(rn1(20) + 1);
			}
			break;
		case P_INFRAVISION:
			if (buc == B_NONE)
			{
				AddMsg("Your eyes feel more sensitive.");
				PromptID(inv[invidx].type);
				PlyrInfravis(50 + rn3(75));
			}
			else if (buc == B_CURSED)
			{
				AddMsg("Your eyes water.");
				PromptID(inv[invidx].type);
				PlyrInfravis(10 + rn3(25));
			}
			else if (buc == B_UNHOLY)
			{
				AddMsg("You suddenly notice monsters.");
				PromptID(inv[invidx].type);
				PlyrInfravis(10 + rn3(15));
				count = 2 + rn3(4);
				for (idx = 0; idx < count; idx++)
				{
					MonGenNear(((plyr.xlvl + plyrmon.dlvl) >> 2) - 1, plyrmon.x, plyrmon.y);
				}
			}
			else if (buc == B_HOLY)
			{
				AddMsg("Your eyes feel more sensitive.");
				PromptID(inv[invidx].type);
				PlyrInfravis(200 + rn3(500));
			}
			break;
		case P_GAIN_LEVEL:
			if (buc == B_NONE || buc == B_HOLY)
			{
				if (plyr.xlvl >= XLVL_CAP)
				{
					AddMsg("You feel as experienced as you can be.");
				}
				else
				{
					AddMsg("You feel more experienced.");
					PlyrExp(XLVL_XP[plyr.xlvl + 1] - plyr.xp);
				}
				Identify(inv[invidx].type);
			}
			else if (buc == B_CURSED)
			{
				AddMsg("You feel a little more experienced.");
				Identify(inv[invidx].type);
				PlyrExp(1 + rn1(20));
			}
			else if (buc == B_UNHOLY)
			{
				AddMsg("You feel set back.");
				PromptID(inv[invidx].type);
				plyr.xp = XLVL_XP[plyr.xlvl];
			}
			break;
	}

	DecrInv(invidx, 1);

	return 1;
}

uint8_t PlyrWield(uint8_t invidx)
{
	if (ITEMDEFS[inv[invidx].type].type != I_WEAPON)
	{
		AddMsg("You don't know how to wield that.");
		return 0;
	}
	if (invwielded != INV_NONE && inv[invwielded].holiness == B_CURSED)
	{
		AddMsg("You can't. You cannot release your current weapon.");
		inv[invidx].knownholy = inv[invidx].holiness;
		// you waste a turn fumbling around with your weapon
		return 1;
	}
	AddMsg("You wield %s.", ItemDesc(&inv[invidx], INV_NONE));
	if (inv[invidx].holiness == B_CURSED)
	{
		AddMsg("Your weapon feels fixed in your grasp.");
	}
	invwielded = invidx;

	return 1;
}

uint8_t PlyrWear(uint8_t invidx)
{
	uint8_t slot;

	if (ITEMDEFS[inv[invidx].type].type != I_ARMOR)
	{
		AddMsg("You can't wear that.");
		return 0;
	}
	slot = ITEMDEFS[inv[invidx].type].slot;
	if (invworn[slot] != INV_NONE)
	{
		switch (slot)
		{
			case S_BODY:
				AddMsg("You can't. You are already wearing body armor.");
				break;
			case S_HELM:
				AddMsg("You can't. You are already wearing a helm.");
				break;
			case S_BOOTS:
				AddMsg("You can't. You are already wearing boots.");
				break;
			case S_GLOVES:
				AddMsg("You can't. You are already wearing gloves.");
				break;
			case S_RING:
				AddMsg("You can't. You are already wearing a ring.");
				break;
			case S_CLOAK:
				AddMsg("You can't. You are already wearing a cloak.");
				break;
		}
		return 0;
	}
	invworn[slot] = invidx;
	if (slot == S_BODY || slot == S_BOOTS || slot == S_GLOVES)
	{
		AddMsg("You put on some %s.", SLOTNAMES[slot]);
	}
	else
	{
		AddMsg("You put on a %s.", SLOTNAMES[slot]);
	}

	return 1;
}

uint8_t PlyrTakeoff(uint8_t invidx)
{
	uint8_t slot;

	slot = ITEMDEFS[inv[invidx].type].slot;
	if (inv[invidx].holiness == B_UNHOLY)
	{
		if (slot == S_BOOTS || slot == S_GLOVES)
		{
			AddMsg("Your %s resist your attempts to take them off.", SLOTNAMES[slot]);
		}
		else
		{
			AddMsg("Your %s resists your attempts to take it off.", SLOTNAMES[slot]);
		}
		inv[invidx].knownholy = inv[invidx].holiness;
		return 1;
	}
	invworn[slot] = INV_NONE;
	AddMsg("You take off your %s.", SLOTNAMES[slot]);

	return 1;
}
